package state.bluetooth;

import java.io.IOException;
import java.util.Vector;

import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;

public class BluetoothUtil {
	/** Attribute ID for service name for base language */
	public static final int SERVICE_NAME_BASE_LANGUAGE = 0x0100;
	
	protected static final long DEFAULT_TIME_OUT_TO_DISCONNECT = 3000;
	protected static final long HEART_BREATH_SEQUENCE_TIME = 1000;

	private Discover discover;
	private BluetoothServer server;
	private String localName = "No name";
	private LocalDevice localDevice;
	private DiscoveryAgent agent;
	private RemoteDevice currentDevice;

	public BluetoothUtil() throws BluetoothStateException {
		localDevice = LocalDevice.getLocalDevice();
		agent = this.localDevice.getDiscoveryAgent();
		discover = new Discover(agent);
	}

	public BluetoothServer startServer(String serviceUUID, ServerListener serverListener, ProtocolListener listener) throws BluetoothStateException {
		if (server != null) {
			server.detroy();
		}
		server = new BluetoothServer(serviceUUID, localName, localDevice, serverListener, listener);
		return server;
	}

	public void stopServer() {
		if (server != null) {
			server.detroy();
			server = null;
		}
	}

	public void findServer(String serviceUUID, ClientListener clientListener, ProtocolListener listener) throws BluetoothStateException {
		discover.findServer(serviceUUID, clientListener, listener);
	}

	public void cancelFindServer() {
		discover.cancelFindServer();
	}

	public void setLocalName(String localName) {
		this.localName = localName;
	}

	public String getLocalName() {
		return localName;
	}

	public void detroy() {
		cancelFindServer();
		stopServer();
		if (discover != null) {
			discover.detroy();
			discover = null;
		}
		localName = null;
		localDevice = null;
		agent = null;
		currentDevice = null;
	}
	
	private class Discover implements DiscoveryListener {
		private String serviceUUID;
		private DiscoveryAgent agent = null;
		private Vector devicesFound = new Vector();
		private int currentTransID;
		private ProtocolListener listener;
		private ClientListener clientListener;

		public Discover(DiscoveryAgent agent) throws BluetoothStateException {
			this.agent = agent;
		}
		
		public void findServer(String serviceUUID, ClientListener clientListener, ProtocolListener listener) throws BluetoothStateException {
			this.clientListener = clientListener;
			this.serviceUUID = serviceUUID;
			this.listener = listener;
			devicesFound = new Vector();
			currentTransID = -1;

			new Thread() {
				public void run() {
					// Bắt đầu tìm kiếm device
					try {
						agent.startInquiry(DiscoveryAgent.GIAC, discover);
					} catch (BluetoothStateException e) {
						try {
							Discover.this.clientListener.onFindServerCompleted(-1);
						} catch (Throwable ex) {
							ex.printStackTrace();
						}
					}
				}
			}.start();
		}

		public void cancelFindServer() {
			agent.cancelInquiry(this);
			if (currentTransID != -1) {
				agent.cancelServiceSearch(currentTransID);
			}
		}

		public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
			if (!devicesFound.contains(btDevice)) {
				devicesFound.addElement(btDevice);
			}
		}

		public void inquiryCompleted(int discType) {
			switch (discType) {
			case DiscoveryListener.INQUIRY_COMPLETED:
				// Inquiry completed normally
				nextDeviceServiceSearch(); // Bắt đầu quá trình tìm kiếm service trên các device
				break;
			case DiscoveryListener.INQUIRY_ERROR:
				try {
					clientListener.onFindServerCompleted(-1);
				} catch (Throwable ex) {
					ex.printStackTrace();
				}
				break;
			case DiscoveryListener.INQUIRY_TERMINATED:
				// Inquiry terminated by agent.cancelInquiry()
				try {
					clientListener.onFindServerCompleted(0);
				} catch (Throwable ex) {
					ex.printStackTrace();
				}
				break;
			}
		}

		/**
		 * NextDeviceServiceSearch. Retrieves the next device from the stored list and searches for the Service on this device.
		 */
		private void nextDeviceServiceSearch() {
			// Nếu đã kết thúc tìm kiếm service trên tất cả các device
			if (devicesFound.size() == 0) {
				try {
					clientListener.onFindServerCompleted(1);
				} catch (Throwable ex) {
					ex.printStackTrace();
				}
				return;
			}

			// Nếu còn device, tiến hành tìm kiếm service trên device đầu tiên
			UUID[] uuid = new UUID[1];
			uuid[0] = new UUID(serviceUUID, false);
			int attrbs[] = {SERVICE_NAME_BASE_LANGUAGE}; // Retrieved service record should include service name

			// Retrieve next device
			currentDevice = (RemoteDevice) devicesFound.firstElement();
			devicesFound.removeElementAt(0);

			// search for the service
			try {
				currentTransID = agent.searchServices(attrbs, uuid, currentDevice, this);
			} catch (Throwable e) {
				nextDeviceServiceSearch();
			}
		}

		public void serviceSearchCompleted(int transID, int respCode) {
			nextDeviceServiceSearch();
		}

		public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
			if ((servRecord == null) || (servRecord.length == 0)) {
				return;
			}
			
			String url = servRecord[0].getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
			String remoteName = (String) servRecord[0].getAttributeValue(SERVICE_NAME_BASE_LANGUAGE).getValue();
			try {
				BluetoothConnection bluetoothConnection = new BluetoothConnection(url, localName, remoteName, listener);
				bluetoothConnection.setRemoteDeviceName(currentDevice.getFriendlyName(false));
				bluetoothConnection.send(localName); // Send name to remote device
				bluetoothConnection.setRemoteName(bluetoothConnection.recieveString());
				bluetoothConnection.start();
				try {
					clientListener.onNewServerFound(new BluetoothClient(bluetoothConnection));
				} catch (Throwable ex) {
					ex.printStackTrace();
				}
			} catch (IOException e) {
			}
		}
		
		public void detroy() {
			serviceUUID = null;
			agent = null;
			devicesFound = null;
			listener = null;
			clientListener = null;
		}
	}
}
